/**
 * Copyright (c) Tapas Mobile.  All Rights Reserved.
 *
 * @author DaiHui
 * @version 1.0
 */

package com.dotools.thread;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;

import com.dotools.base.CommonConstants;
import com.dotools.utils.DevicesUtils;
import com.dotools.utils.ThreadUtils;

public class ThreadPool {
    private static DynamicThreadPool sDynamicPool = null;
    private static DynamicThreadPool sDynamicCriticalPool = null;

    private static ScheduledThreadPoolExecutor scheduledPool = new ScheduledThreadPoolExecutor(1);

    private static Handler sUiHandler = null;
    private static HandlerThread sHandlerThread = null;
    private static Handler sWorkerHandler = null;

    public static void runOnPool(Runnable r) {
        if (CommonConstants.IS_DEBUG)
            sDynamicPool.execute(new ShowExceptionRunnable(r));
        else
            sDynamicPool.execute(r);
    }

    public static Executor getPoolExecutor() {
        return sDynamicPool;
    }

    public static void runOnUi(Runnable r) {
        if (sUiHandler != null)
            sUiHandler.post(r);
    }

    public static void postOnUiDelayed(Runnable r, int delay) {
        if (CommonConstants.IS_DEBUG)
            sUiHandler.postDelayed(r, delay);
        else
            sUiHandler.postDelayed(r, delay);
    }

    public static void runCriticalTask(Runnable r) {
        if (CommonConstants.IS_DEBUG) {
            // LOG.logD("[CRITICAL TASK SUBMITED: " + r, new Exception());
            sDynamicCriticalPool.execute(new ShowExceptionRunnable(r));
        } else {
            // LOG.logD("[CRITICAL TASK SUBMITED: " + r);
            sDynamicCriticalPool.execute(r);
        }
    }

    public static void runOnWorker(Runnable r) {
        if (CommonConstants.IS_DEBUG)
            sWorkerHandler.post(new ShowExceptionRunnable(r));
        else
            sWorkerHandler.post(r);
    }

    public static void postOnWorkerDelayed(Runnable r, int delay) {
        if (CommonConstants.IS_DEBUG)
            sWorkerHandler.postDelayed(new ShowExceptionRunnable(r), delay);
        else
            sWorkerHandler.postDelayed(r, delay);
    }

    public static Looper getWorkerLooper() {
        return sHandlerThread.getLooper();
    }

    public static Handler getWorkerHandler() {
        return sWorkerHandler;
    }

    /**
     * Schedule a runnable running at fixed rate
     */
    public static ScheduledFuture<?> schedule(Runnable r, long initialDelay, long period, TimeUnit unit) {
        if (CommonConstants.IS_DEBUG)
            return scheduledPool.scheduleAtFixedRate(new ShowExceptionRunnable(r), initialDelay, period, TimeUnit.SECONDS);
        else
            return scheduledPool.scheduleAtFixedRate(r, initialDelay, period, TimeUnit.SECONDS);
    }

    public static void startup() {
        ThreadUtils.ensureUiThread();

        // standard pool runner
        final int minThreads = Math.max(2, CommonConstants.CPU_CORES);
        final int maxThreads = Math.max(4, CommonConstants.CPU_CORES * 2);
        sDynamicPool = new DynamicThreadPool(new LinkedBlockingQueue<Runnable>(), minThreads, maxThreads, 0,
                Thread.MIN_PRIORITY + 1);

        AsyncTask.setDefaultExecutor(sDynamicPool);

        // critical pool runner
        final int minThreadsForCritical = Math.max(2, CommonConstants.CPU_CORES);
        final int maxThreadsForCritical = DevicesUtils.lowPhysicalMemoryDevices() ? CommonConstants.CPU_CORES + 1 : CommonConstants.CPU_CORES * 2;
        sDynamicCriticalPool = new DynamicThreadPool(new LinkedBlockingQueue<Runnable>(), minThreadsForCritical,
                maxThreadsForCritical, 0, Thread.NORM_PRIORITY - 1);

        // ui thread runner
        sUiHandler = new Handler();

        // handler based thread runner
        sHandlerThread = new HandlerThread("internal");
        sHandlerThread.setPriority(Thread.NORM_PRIORITY - 1);
        sHandlerThread.start();
        sWorkerHandler = new Handler(sHandlerThread.getLooper());
    }

    public static void shutdown() {
        sHandlerThread.quit();
    }
}
